package com.hirayclay.incrementalprogressbar

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.*
import android.support.annotation.FloatRange
import android.util.AttributeSet
import android.util.Log
import android.view.View
import java.lang.IllegalStateException

class IncrementalProgressBar(context: Context, attrs: AttributeSet) : View(context, attrs), View.OnClickListener {
    private val TAG = "IncrementalProgressBar"
    //进度线条宽度
    private var barThickness: Float
    //进度条颜色
    private var barColor: Int
    //背景遮罩颜色
    private var barMaskColor: Int
    //进度条半径
    private var barRadius: Float
    //进度跳进度偏移（决定起始角度）
    private var sAngle = 0f
    //当前进度
    @FloatRange(from = 0.0, to = 1.0)
    private var progress: Float = 0f

    private var paint: Paint

    private var outerPaint: Paint

    private var indicatorPaint: Paint

    private val STATE_IDLE = 0

    private val STATE_LOADING = 1

    private val STATE_COMPLETING_ANIMATION = 2

    private val STATE_COMPLETED = 3

    private var state = STATE_IDLE

    private var isSmoothProgress: Boolean = false

    var completeValue: Float = 0f
        get() = field
        set(value) {
            field = value
            invalidate()
            Log.i(TAG, ": ")
        }

    init {
        val typeArray = context.obtainStyledAttributes(attrs, R.styleable.IncrementalProgressBar)
        barThickness = typeArray.getFloat(R.styleable.IncrementalProgressBar_bar_thickness, 5f)
        barColor = typeArray.getColor(R.styleable.IncrementalProgressBar_bar_color, Color.WHITE)
        barMaskColor = typeArray.getColor(R.styleable.IncrementalProgressBar_bar_mask_color, Color.parseColor("#e6e6e6"))
        barRadius = typeArray.getDimensionPixelSize(R.styleable.IncrementalProgressBar_bar_radius, 28).toFloat()
        typeArray.recycle()
        paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.color = barColor
        paint.strokeWidth = barThickness
        paint.style = Paint.Style.STROKE
        paint.strokeJoin = Paint.Join.ROUND
        paint.strokeCap = Paint.Cap.ROUND

        outerPaint = Paint(paint)
        outerPaint.color = Color.parseColor("#8A8A8A")
        outerPaint.style = Paint.Style.FILL_AND_STROKE

        indicatorPaint = Paint(paint)
        indicatorPaint.style = Paint.Style.FILL
        indicatorPaint.strokeJoin = Paint.Join.BEVEL
        indicatorPaint.strokeCap = Paint.Cap.ROUND
        setOnClickListener(this@IncrementalProgressBar)
    }

    override fun onDraw(canvas: Canvas) {
        if (isCompleting()) {
            update(canvas)
        }

        sAngle++

        drawBackground(canvas)

        drawOuterCircleMask(canvas)

        drawActionIndicator(canvas)

        drawProgressBar(canvas)

        if (isCompleted())
            invalidate()
        else
            postInvalidateDelayed(64)
    }

    fun update(canvas: Canvas) {

    }

    private fun drawOuterCircleMask(canvas: Canvas) {
        val x = measuredWidth / 2f
        val y = measuredHeight / 2f
        canvas.drawCircle(x, y, barRadius + 4f, outerPaint)
    }


    private fun drawActionIndicator(canvas: Canvas) {
        val r = 0.3f * barRadius
        val leftPointX = measuredWidth / 2 - r
        val leftPointY = measuredHeight / 2 - r
        val x = measuredWidth / 2
        val y = measuredHeight / 2
        when (state) {
            STATE_IDLE -> {
                val path = Path()
                path.moveTo(x - 0.15f * barRadius, y - 0.55f * barRadius)
                path.rLineTo(0f, 0.8f * barRadius)
                path.rLineTo(-0.15f * barRadius, 0f)
                path.rLineTo(0.3f * barRadius, 0.3f * barRadius)
                path.rLineTo(0.3f * barRadius, -0.3f * barRadius)
                path.rLineTo(-0.15f * barRadius, 0f)
                path.rLineTo(0f, -0.8f * barRadius)
                path.close()
                canvas.drawPath(path, indicatorPaint)
            }
            STATE_LOADING -> {
                canvas.drawLine(leftPointX, leftPointY, leftPointX + r * 2, leftPointY + r * 2, paint)
                canvas.drawLine(leftPointX + r * 2, leftPointY, leftPointX, leftPointY + r * 2, paint)
            }

            STATE_COMPLETED -> {
                val meter = 0.5f * barRadius
                val chord = 1.732f * meter  //√3 * meter
                val halfChord = chord / 2
                val halfR = meter / 2f
                //path
                val path = Path()
                path.moveTo(x - halfR, y - halfChord)
                path.rLineTo(0f, chord)
                path.rLineTo(1.5f * meter, -halfChord)
                path.close()
                canvas.drawPath(path, indicatorPaint)
            }
        }

    }

    private fun drawBackground(canvas: Canvas) {
        canvas.drawColor(barMaskColor)
    }

    private fun drawProgressBar(canvas: Canvas) {
        if (isCompleted() || isIdle())
            return
        val startAngle = sAngle
        val sweepAngle = 360 * progress
        canvas.drawArc(createRectF(measuredWidth / 2 - barRadius, measuredHeight / 2 - barRadius, barRadius), startAngle, sweepAngle, false, paint)
    }


    fun setProgress(progress: Float) {
        if (progress < 0f)
            throw IllegalStateException("progress cannot be less than zero ,but you set it $progress")
        if (animateProgressIfNeed(progress))
            return
        this.progress = progress
        state = when {
            progress == 0f -> STATE_IDLE
            progress < 1f -> STATE_LOADING
            else -> {
                complete()
                STATE_COMPLETED

            }
        }
        postInvalidate()
    }


    inner class SmoothProgressRunnable(private var finalProgress: Float, private var currProgress: Float) : Runnable {

        override fun run() {
            isSmoothProgress = true
            val animator = ObjectAnimator.ofFloat(this@IncrementalProgressBar, "completeValue", currProgress, finalProgress)
                    .setDuration((120 * (finalProgress - currProgress)).toLong())
            animator.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    isSmoothProgress = false
                }
            })
            animator.start()
        }

    }


    private fun animateProgressIfNeed(progress: Float): Boolean {
        var r = false
        //progress run fast over short time ,we need an animation to run progress smooth
        if (progress - this.progress > 0.4f) {
            handler.post(SmoothProgressRunnable(progress, this.progress))
            this.progress = progress
            r = true
        }
        return r
    }

    private fun cancel() {
        progress = 0f
        state = STATE_IDLE
        invalidate()
    }

    fun start() {
        invalidate()
        state = STATE_LOADING
    }

    private fun complete() {
        state = STATE_COMPLETING_ANIMATION
        ObjectAnimator.ofFloat(this, "completeValue", progress, 1.0f)
                .setDuration(200)
                .start()

    }


    override fun onClick(v: View) {

    }

    private fun isIdle() = state == STATE_IDLE
    private fun isCompleted() = state == STATE_COMPLETED
    private fun isCompleting() = state == STATE_COMPLETING_ANIMATION

    private fun createRectF(x: Float, y: Float, r: Float) = RectF(x, y, x + r * 2, y + r * 2)
}